

package cn.shengchao.examstar.pay.acl.adapters.repositories;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.shengchao.base.event.PayRefundMessageEvent;
import cn.shengchao.base.event.PaySuccessMessageEvent;
import cn.shengchao.base.lock.DistributedLock;
import cn.shengchao.base.lock.ZLock;
import cn.shengchao.examstar.pay.acl.ports.repositories.IOrderRepository;
import cn.shengchao.examstar.pay.infrastructure.dao.*;
import cn.shengchao.examstar.pay.infrastructure.dao.po.*;
import cn.shengchao.examstar.pay.model.aggregate.CreateOrderAggregate;
import cn.shengchao.examstar.pay.model.entity.*;
import cn.shengchao.examstar.pay.model.valobj.OrderStatusVO;
import cn.shengchao.examstar.rpc.distribute.DistributeOrderTaskApi;
import cn.shengchao.examstar.rpc.orders.req.*;
import cn.shengchao.examstar.rpc.orders.res.*;
import cn.shengchao.examstart.user.infrastructure.dao.UserDao;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import top.continew.starter.core.validation.CheckUtils;

import java.math.BigDecimal;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Repository
public class OrderRepository implements IOrderRepository {

    @Autowired
    private OrderDao orderDao;


    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private PayOrderItemDao payOrderItemDao;

    @Autowired
    private PayOrderStatusLogDao payOrderStatusLogDao;

    @Autowired
    private PayOrderRemarksDao payOrderRemarksDao;

    @Autowired
    private PayOrderRefundDao payOrderRefundDao;

    @Autowired
    private UserDao userDao;

    @Autowired
    private DistributedLock distributedLock;

    @DubboReference
    private DistributeOrderTaskApi distributeOrderTaskApi;

    @Override
    public OrderCountInfo countUserPay(Long userId) {
        return orderDao.countUserPay(userId);
    }

    @Override
    public PayOrder queryOrderInfoById(Long orderId) {
        return orderDao.selectOne(Wrappers.lambdaQuery(PayOrder.class).eq(PayOrder::getOrderId,orderId.toString()));
    }
    @Override
    public List<PayOrderItem> queryOrderItemInfosById(Long orderId) {
        return payOrderItemDao.selectList(Wrappers.lambdaQuery(PayOrderItem.class).eq(PayOrderItem::getOrderId,orderId.toString()));
    }
    @Override
    public OrderEntity queryUnPayOrder(ShopCartEntity shopCartEntity) {
        // 2. 查询到订单
        PayOrder order = orderDao.selectOne(new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getUserId, shopCartEntity
                        .getUserId())
                .eq(PayOrder::getProductId, shopCartEntity.getProductId())
                .ne(PayOrder::getStatus, OrderStatusVO.CLOSE.getCode())
                .orderByDesc(PayOrder::getOrderTime)
                .last(" limit 1"));
        if (null == order)
            return null;
        List<PayOrderItem> list = payOrderItemDao.selectList(new LambdaQueryWrapper<PayOrderItem>()
                .eq(PayOrderItem::getOrderId, order.getOrderId()));
        List<Long> productIds = list.stream()
                .map(PayOrderItem::getProductId)
                .map(Long::valueOf)
                .sorted(Comparator.comparingLong(Long::intValue))
                .toList();
        //子订单相同
        if (productIds != shopCartEntity.getSubProductIds()
                .stream()
                .sorted(Comparator.comparingLong(Long::intValue))
                .toList()) {
            return null;
        }
        // 3. 返回结果
        return OrderEntity.builder()
                .productId(order.getProductId())
                .productName(order.getProductName())
                .productType(order.getProductType())
                .orderId(order.getOrderId())
                .orderStatus(OrderStatusVO.valueOf(order.getStatus()))
                .orderTime(order.getOrderTime())
                .totalAmount(order.getTotalAmount())
                .payInfo(order.getPayInfo())
                .build();
    }

    @Transactional
    @Override
    public void doSaveOrder(CreateOrderAggregate orderAggregate) {
        String userId = orderAggregate.getUserId();
        ProductEntity productEntity = orderAggregate.getProductEntity();
        OrderEntity orderEntity = orderAggregate.getOrderEntity();
        PayOrder order = new PayOrder();
        order.setUserId(userId);
        order.setProductId(productEntity.getProductId());
        order.setProductName(productEntity.getProductName());
        order.setOrderId(orderEntity.getOrderId());
        order.setOrderTime(orderEntity.getOrderTime());
        order.setTotalAmount(orderEntity.getTotalAmount());
        order.setPrice(productEntity.getPrice());
        order.setStatus(orderEntity.getOrderStatus().getCode());
        order.setShareCode(orderAggregate.getShareCode());
        order.setProductInfo(orderEntity.getProductInfo());
        order.setDiscountInfo(orderEntity.getDiscountInfo());
        order.setProductType(productEntity.getProductType());
        order.setPayType(orderEntity.getPayType());
        orderDao.insert(order);
        // 保存子订单
        List<PayOrderItem> payOrderItems = new ArrayList<>();
        for (OrderItemEntity orderItemEntity : orderEntity.getOrderItemEntities()) {
            PayOrderItem payOrderItem = new PayOrderItem();
            payOrderItem.setProductId(orderItemEntity.getProductId());
            payOrderItem.setUserId(userId);
            payOrderItem.setProductName(orderItemEntity.getProductName());
            payOrderItem.setOrderId(orderItemEntity.getOrderId());
            payOrderItem.setOrderTime(orderItemEntity.getOrderTime());
            payOrderItem.setTotalAmount(orderItemEntity.getTotalAmount());
            payOrderItem.setPrice(orderItemEntity.getPrice());
            payOrderItem.setProductInfo(orderItemEntity.getProductInfo());
            payOrderItems.add(payOrderItem);
        }
        payOrderItemDao.insert(payOrderItems);
        // 保存订单状态日志
        PayOrderStatusLog payOrderStatusLog = new PayOrderStatusLog();
        payOrderStatusLog.setOrderId(Long.valueOf(order.getOrderId()));
        payOrderStatusLog.setStatus(order.getStatus());
        payOrderStatusLog.setStatusDescription(orderEntity.getOrderStatus().getDesc());
        payOrderStatusLog.setChangeTime(new Date());
        payOrderStatusLogDao.insert(payOrderStatusLog);
    }

    @Override
    public void updateOrderPayInfo(PayOrderEntity payOrderEntity) {
        PayOrder order = new PayOrder();
        order.setUserId(payOrderEntity.getUserId());
        order.setOrderId(payOrderEntity.getOrderId());
        order.setPayInfo(JSON.toJSONString(payOrderEntity.getPayInfo()));
        order.setStatus(payOrderEntity.getOrderStatus().getCode());
        orderDao.update(order, new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getOrderId, payOrderEntity
                .getOrderId()));
        // 保存订单状态日志
        PayOrderStatusLog payOrderStatusLog = new PayOrderStatusLog();
        payOrderStatusLog.setOrderId(Long.valueOf(order.getOrderId()));
        payOrderStatusLog.setStatus(order.getStatus());
        payOrderStatusLog.setStatusDescription(payOrderEntity.getOrderStatus().getDesc());
        payOrderStatusLog.setChangeTime(new Date());
        payOrderStatusLogDao.insert(payOrderStatusLog);
    }

    @Override
    public void changeOrderPaySuccess(String orderId) {
        PayOrder order;
        order = orderDao.selectOne(new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getOrderId, orderId));
        if (!order.getStatus().equals(OrderStatusVO.PAY_WAIT.getCode())&& !"0".equals(order.getPayType())) {
            return;
        }
        order.setStatus(OrderStatusVO.PAY_SUCCESS.getCode());
        order.setPayTime(new Date());
        orderDao.update(order, new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getOrderId, orderId));

        List<PayOrderItem> payOrderItems = payOrderItemDao.selectList(new LambdaQueryWrapper<PayOrderItem>()
                .eq(PayOrderItem::getOrderId, orderId));
        // 保存订单状态日志
        PayOrderStatusLog payOrderStatusLog = new PayOrderStatusLog();
        payOrderStatusLog.setOrderId(Long.valueOf(order.getOrderId()));
        payOrderStatusLog.setStatus(order.getStatus());
        payOrderStatusLog.setStatusDescription(OrderStatusVO.PAY_SUCCESS.getDesc());
        payOrderStatusLog.setChangeTime(new Date());
        payOrderStatusLogDao.insert(payOrderStatusLog);
        // 发送MQ消息
        PaySuccessMessageEvent eventMessage = new PaySuccessMessageEvent(PaySuccessMessageEvent.PaySuccessMessage
                .builder()
                .userId(order.getUserId())
                .productType(order.getProductType())
                .productId(order.getProductId())
                .tradeNo(orderId)
                //添加上支付方式，0是免费解锁的
                .payType(order.getPayType())
                .amount(order.getTotalAmount())
                .shareCode(order.getShareCode())
                .subProductIds(payOrderItems.stream().map(PayOrderItem::getProductId).toList())
                .payTime(order.getPayTime())
                .build());
        applicationEventPublisher.publishEvent(eventMessage);
    }

    @Override
    public List<PayOrder> queryNoPayNotifyOrder() {
        return orderDao.selectList(new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getStatus, OrderStatusVO.PAY_WAIT
                        .getCode()).lt(PayOrder::getOrderTime, new Date(System.currentTimeMillis() - 60 * 1000)))
                .stream()
                .toList();
    }

    @Override
    public List<String> queryTimeoutCloseOrderList() {
        return orderDao.selectList(new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getStatus, OrderStatusVO.PAY_WAIT
                        .getCode()).lt(PayOrder::getOrderTime, new Date(System.currentTimeMillis() - 30 * 60 * 1000)))
                .stream()
                .map(PayOrder::getOrderId)
                .toList();
    }

    @Override
    public boolean changeOrderClose(String orderId) {
        PayOrder order = new PayOrder();
        order.setOrderId(orderId);
        order.setStatus(OrderStatusVO.CLOSE.getCode());
        return orderDao.update(order, new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getOrderId, orderId)) > 0;
    }

    @Override
    public List<OrderList> getOrderList(OrderQueryPage orderQueryPage) {
        return orderDao.getOrderList(orderQueryPage);
    }
    @Override
    public List<OrderList> getOrderImgList(List<Long> orderIds) {
        return orderDao.getOrderImgList(orderIds);
    }
    @Override
    public List<OrderItemInfo> queryOrderItemList(String orderId) {
        return orderDao.queryOrderItemList(orderId);
    }

    @Override
    public List<OrderChangeLog> queryOrderChangeLogs(String orderId) {
        List<PayOrderStatusLog> payOrderStatusLogs = payOrderStatusLogDao
                .selectList(new LambdaQueryWrapper<PayOrderStatusLog>().eq(PayOrderStatusLog::getOrderId, orderId));
        return payOrderStatusLogs.stream().map(payOrderStatusLog -> {
            OrderChangeLog orderChangeLog = new OrderChangeLog();
            orderChangeLog.setChangeTime(LocalDateTimeUtil.of(payOrderStatusLog.getChangeTime()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss",Locale.CHINA)));
            orderChangeLog.setStatusDesc(payOrderStatusLog.getStatusDescription());
            return orderChangeLog;
        }).toList();
    }

    @Override
    public void saveOrderRemarks(Long orderId, String remarks, Long userId) {
        PayOrderRemarks payOrderRemarks = new PayOrderRemarks();
        payOrderRemarks.setOrderId(orderId);
        payOrderRemarks.setRemarks(remarks);
        payOrderRemarks.setCreateBy(userId);
        payOrderRemarks.setTime(new Date());
        payOrderRemarksDao.insert(payOrderRemarks);
    }

    @Override
    public List<Remarks> getOrderRemarks(String orderId) {
        List<PayOrderRemarks> payOrderRemarks = payOrderRemarksDao.selectList(new LambdaQueryWrapper<PayOrderRemarks>()
                .eq(PayOrderRemarks::getOrderId, orderId));
        return payOrderRemarks.stream().map(item -> {
            Remarks remarks = new Remarks();
            remarks.setRemarks(item.getRemarks());
            remarks.setTime(LocalDateTimeUtil.of(item.getTime()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss",Locale.CHINA)));

            remarks.setUserName(userDao.selectById(item.getCreateBy()).getNickname());
            return remarks;
        }).toList();
    }

    @Override
    public OrderEntity queryOrder(String orderId) {
        PayOrder order = orderDao.selectOne(new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getOrderId, orderId));
        if (order != null) {
            OrderEntity orderEntity = new OrderEntity();
            orderEntity.setOrderId(order.getOrderId());
            orderEntity.setOrderTime(order.getOrderTime());
            orderEntity.setOrderStatus(OrderStatusVO.valueOf(order.getStatus()));
            orderEntity.setProductId(order.getProductId());
            orderEntity.setProductName(order.getProductName());
            orderEntity.setTotalAmount(order.getTotalAmount());
            orderEntity.setPrice(order.getPrice());
            return orderEntity;
        }
        return null;
    }

    @Override
    public BigDecimal getOrderRefundsSum(String orderId) {
        // 已退费金额
        return payOrderRefundDao.selectList(new LambdaQueryWrapper<PayOrderRefund>()
                        .eq(PayOrderRefund::getOrderId, orderId)
                        .ne(PayOrderRefund::getStatus, 3))
                .stream()
                .map(PayOrderRefund::getPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    @Transactional
    @Override
    public void refunds(Long operate,
                        String orderId,
                        Long orderItemId,
                        BigDecimal refundPrice,
                        String refundType,
                        String remarks,
                        boolean isAll) throws Exception {
        // 使用分布式锁确保同一订单退款操作的幂等性
        String lockKey = "ORDER_REFUND_LOCK:" + orderId;
        ZLock zLock = null;
        try {
            // 尝试获取锁，等待最多3秒，锁定30秒
            zLock = distributedLock.tryLock(lockKey, 3, 30, TimeUnit.SECONDS);
            CheckUtils.throwIf(zLock == null, "订单退款处理中，请稍后重试");
            BigDecimal refundSum = getOrderRefundsSum(orderId);
            //查询订单信息
            PayOrder order = orderDao.selectOne(new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getOrderId, orderId));
            CheckUtils.throwIf(order == null, "订单不存在");
            // 防重复处理：检查订单状态
            // 全额退费时，检查订单是否已经是全额退费状态
            CheckUtils.throwIf(Objects.equals(order.getStatus(), OrderStatusVO.REFUND.getCode()), "订单已全额退款，请勿重复操作");
            //更新订单状态为退费
            order.setStatus(isAll ? OrderStatusVO.REFUND.getCode() : OrderStatusVO.PARTIAL_REFUND.getCode());

            List<Long> orderItems = new ArrayList<>();
            //存在id，则更新订单项状态为退费
            if (orderItemId != null) {

                PayOrderItem orderItem = payOrderItemDao.selectOne(new LambdaQueryWrapper<PayOrderItem>()
                        .eq(PayOrderItem::getId, orderItemId)
                        .and(wrapper -> wrapper.ne(PayOrderItem::getStatus, OrderStatusVO.REFUND.getCode())
                                .or()
                                .isNull(PayOrderItem::getStatus)));
                CheckUtils.throwIf(orderItem == null, "订单已退费！");

                //0元订单不退费
                if (orderItem != null && orderItem.getTotalAmount().doubleValue() > 0) {
                    CheckUtils.throwIf(Objects.equals(orderItem.getStatus(), OrderStatusVO.REFUND.getCode()), "订单项已退款");
                    orderItem.setStatus(OrderStatusVO.REFUND.getCode());
                    payOrderItemDao.updateById(orderItem);
                    long productItemId = Long.parseLong(orderItem.getProductId());
                    orderItems.add(productItemId);
                    refundSum = refundSum.add(orderItem.getTotalAmount());
                    //退费记录
                    PayOrderRefund payOrderRefund = new PayOrderRefund();
                    payOrderRefund.setOrderId(Long.valueOf(orderId));
                    payOrderRefund.setOrderItemId(orderItemId);
                    payOrderRefund.setPrice(orderItem.getTotalAmount());
                    payOrderRefund.setStatus("2");
                    payOrderRefund.setTime(new Date());
                    payOrderRefund.setRefundType(refundType);
                    payOrderRefund.setRemark(remarks);
                    payOrderRefund.setApplicant(operate);
                    payOrderRefund.setExamine(operate);
                    payOrderRefund.setExamineTime(new Date());
                    payOrderRefundDao.insert(payOrderRefund);
                }
                //0元订单不退费
                if (orderItem == null || orderItem.getTotalAmount().doubleValue() == 0) {
                    return;
                }
            }
            //如果全额退费，则更新订单项状态为退费
            if (isAll) {
                List<PayOrderRefund> payOrderRefunds = new ArrayList<>();
                List<PayOrderItem> payOrderItems = payOrderItemDao.selectList(new LambdaQueryWrapper<PayOrderItem>()
                        .eq(PayOrderItem::getOrderId, orderId).and(wrapper -> wrapper.ne(PayOrderItem::getStatus, OrderStatusVO.REFUND.getCode())
                                .or()
                                .isNull(PayOrderItem::getStatus)));
                for (PayOrderItem payOrderItem : payOrderItems) {
                    if (payOrderItem.getTotalAmount().doubleValue() > 0) {
                        orderItems.add(Long.valueOf(payOrderItem.getProductId()));
                        payOrderItem.setStatus(OrderStatusVO.REFUND.getCode());
                        payOrderItemDao.updateById(payOrderItem);
                        refundSum = refundSum.add(payOrderItem.getTotalAmount());
                        //退费记录（全额）
                        PayOrderRefund payOrderRefund = new PayOrderRefund();
                        payOrderRefund.setOrderId(Long.valueOf(orderId));
                        payOrderRefund.setOrderItemId(payOrderItem.getId());
                        payOrderRefund.setPrice(payOrderItem.getTotalAmount());
                        payOrderRefund.setStatus("2");
                        payOrderRefund.setTime(new Date());
                        payOrderRefund.setRefundType(refundType);
                        payOrderRefund.setApplicant(operate);
                        payOrderRefund.setExamine(operate);
                        payOrderRefund.setExamineTime(new Date());
                        payOrderRefund.setRemark(remarks);
                        payOrderRefunds.add(payOrderRefund);
                    }
                }
                payOrderRefundDao.insert(payOrderRefunds);
            }
            if (order.getTotalAmount().doubleValue() == refundSum.doubleValue()) {
                order.setStatus(OrderStatusVO.REFUND.getCode());
            }
            orderDao.updateById(order);
            // 保存订单状态日志
            PayOrderStatusLog payOrderStatusLog = new PayOrderStatusLog();
            payOrderStatusLog.setOrderId(Long.valueOf(order.getOrderId()));
            payOrderStatusLog.setStatus(order.getStatus());
            payOrderStatusLog.setStatusDescription(OrderStatusVO.valueOf(order.getStatus()).getDesc());
            payOrderStatusLog.setRemarks(remarks);
            payOrderStatusLog.setChangeTime(new Date());
            payOrderStatusLogDao.insert(payOrderStatusLog);
            boolean isSuccess = distributeOrderTaskApi.processOrderRefund(Long.valueOf(order.getOrderId()));
            CheckUtils.throwIf(!isSuccess, "退款失败,处理分销业务发生错误");
            // 发送MQ消息
            PayRefundMessageEvent eventMessage = new PayRefundMessageEvent(PayRefundMessageEvent.PayRefundMessage.builder()
                    .userId(Long.valueOf(order.getUserId()))
                    .productId(Long.valueOf(order.getProductId()))
                    .orderItemIds(orderItems)
                    .orderId(Long.valueOf(orderId))
                    .build());
            applicationEventPublisher.publishEvent(eventMessage);
        } finally {
            // 释放分布式锁
            if (zLock != null) {
                distributedLock.unlock(zLock);
            }
        }
    }

    @Override
    public List<RefundList> getOrderRefund(RefundQueryPage refundQueryPage) {
        return payOrderRefundDao.getOrderRefund(refundQueryPage);
    }

    @Override
    public Optional<BigDecimal> getOrderAmount(Long userId, DateTime startTime, Date endTime) {
        if (userId != null) {
            return orderDao.selectList(new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getUserId, userId)
                            .ge(startTime != null, PayOrder::getOrderTime, startTime)
                            .le(endTime != null, PayOrder::getOrderTime, endTime)
                            .eq(PayOrder::getStatus, OrderStatusVO.PAY_SUCCESS.getCode()))
                    .stream()
                    .map(PayOrder::getTotalAmount)
                    .reduce(BigDecimal::add);
        }
        return Optional.empty();
    }

    @Override
    public Optional<BigDecimal> getOrderAmount(Long userId) {
        return getOrderAmount(userId, null, null);
    }

    @Override
    public List<OrderWithSupplierView> selectOrderViewListByPageQuery(OrderWithSupplierPageQuery orderWithSupplierPageQuery) {
        return orderDao.selectOrderViewListByPageQuery(orderWithSupplierPageQuery);
    }

    @Override
    public Integer countOrderByOrderId(String orderId) {
        return orderDao.countOrderByOrderId(orderId);
    }

    @Override
    public BigDecimal countOrderViewAmountByPageQuery(OrderWithSupplierPageQuery orderWithSupplierPageQuery) {
        return orderDao.countOrderViewAmountByPageQuery(orderWithSupplierPageQuery);
    }

    @Override
    public List<SubOrderWithSupplierView> selectSubOrderViewListByPageQuery(SubOrderWithSupplierPageQuery subOrderWithSupplierPageQuery) {
        return orderDao.selectSubOrderViewListByPageQuery(subOrderWithSupplierPageQuery);
    }

    @Override
    public Map<String,BigDecimal> countSubOrderViewAmountByPageQuery(SubOrderWithSupplierPageQuery subOrderWithSupplierPageQuery) {
        Map<String,BigDecimal> sumAmountMap=new HashMap<>();
        Map<String,BigDecimal> totalAmount=orderDao.countSubOrderViewAmountByPageQuery(subOrderWithSupplierPageQuery);
        Map<String,BigDecimal> distributeAmount=orderDao.countSubOrderViewDistributeAmountByPageQuery(subOrderWithSupplierPageQuery);
        sumAmountMap.put("orderAmount", CollUtil.isNotEmpty(totalAmount) &&totalAmount.containsKey("orderAmount")?totalAmount.get("orderAmount") : BigDecimal.ZERO);
        sumAmountMap.put("distributeAmount", CollUtil.isNotEmpty(distributeAmount)&&distributeAmount.containsKey("distributeAmount") ?distributeAmount.get("distributeAmount"): BigDecimal.ZERO);
        return sumAmountMap;
    }

    @Override
    public List<SubOrderWithSupplierView> selectSubOrderListByOrderId(Long orderId) {
        return orderDao.querySubOrderListById(orderId);
    }

    @Override
    public List<SubOrderWithSupplierView> selectSubOrderListFromMainOrder(SubOrderForMainPageQuery subOrderForMainPageQuery) {
        return orderDao.selectSubOrderListFromMainOrder(subOrderForMainPageQuery);
    }
}
